/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * @author Xiao-Feng Li, 2006/10/05
 */

#include "gc_thread.h"
#include "../gen/gen.h"
#include "../mark_sweep/gc_ms.h"
#include "../move_compact/gc_mc.h"
#include "../finalizer_weakref/finalizer_weakref.h"

#ifdef GC_GEN_STATS
#include "../gen/gen_stats.h"
#endif

//#define GC_OBJ_SIZE_STATISTIC

#ifdef GC_OBJ_SIZE_STATISTIC
#define GC_OBJ_SIZE_STA_MAX 256*KB
unsigned int obj_size_distribution_map[GC_OBJ_SIZE_STA_MAX>>10];
void gc_alloc_statistic_obj_distrubution(unsigned int size)
{
    unsigned int sta_precision = 16*KB;
    unsigned int max_sta_size = 128*KB;
    unsigned int sta_current = 0;    

    assert(!(GC_OBJ_SIZE_STA_MAX % sta_precision));
    assert(!(max_sta_size % sta_precision));    
    while( sta_current < max_sta_size ){
        if(size < sta_current){
            unsigned int index = sta_current >> 10;
            obj_size_distribution_map[index] ++;
            return;
        }
        sta_current += sta_precision;
    }
    unsigned int index = sta_current >> 10;
    obj_size_distribution_map[index]++;
    return;
}
#endif

extern Boolean mutator_need_block;

Managed_Object_Handle gc_alloc(unsigned size, Allocation_Handle ah, void *unused_gc_tls) 
{
  Managed_Object_Handle p_obj = NULL;
 
  /* All requests for space should be multiples of 4 (IA32) or 8(IPF) */
  assert((size % GC_OBJECT_ALIGNMENT) == 0);
  assert(ah);

  size = (size & NEXT_TO_HIGH_BIT_CLEAR_MASK);
  
  Allocator* allocator = (Allocator*)gc_get_tls();
  Boolean type_has_fin = type_has_finalizer((Partial_Reveal_VTable*)decode_vt((VT)ah));
  
  if(type_has_fin && !IGNORE_FINREF && mutator_need_block)
    vm_heavy_finalizer_block_mutator();

#ifdef GC_OBJ_SIZE_STATISTIC
  gc_alloc_statistic_obj_distrubution(size);
#endif

#if defined(USE_UNIQUE_MARK_SWEEP_GC)

  p_obj = (Managed_Object_Handle)gc_ms_alloc(size, allocator);

#elif defined(USE_UNIQUE_MOVE_COMPACT_GC)

  p_obj = (Managed_Object_Handle)gc_mc_alloc(size, allocator);

#else

  if ( size > GC_LOS_OBJ_SIZE_THRESHOLD ){
    p_obj = (Managed_Object_Handle)los_alloc(size, allocator);

#ifdef GC_GEN_STATS
    if (p_obj != NULL){
      GC_Gen* gc = (GC_Gen*)allocator->gc;
      gc->stats->obj_num_los_alloc++;
      gc->stats->total_size_los_alloc += size;
    }
#endif /* #ifdef GC_GEN_STATS */

  }else{
      p_obj = (Managed_Object_Handle)nos_alloc(size, allocator);
  }

#endif /* defined(USE_UNIQUE_MARK_SWEEP_GC) else */

  if( p_obj == NULL )
    return NULL;

  assert((((POINTER_SIZE_INT)p_obj) % GC_OBJECT_ALIGNMENT) == 0);
    
  obj_set_vt((Partial_Reveal_Object*)p_obj, (VT)ah);
  
  if(type_has_fin && !IGNORE_FINREF)
    mutator_add_finalizer((Mutator*)allocator, (Partial_Reveal_Object*)p_obj);
    
  return (Managed_Object_Handle)p_obj;
}


Managed_Object_Handle gc_alloc_fast (unsigned size, Allocation_Handle ah, void *unused_gc_tls) 
{
  /* All requests for space should be multiples of 4 (IA32) or 8(IPF) */
  assert((size % GC_OBJECT_ALIGNMENT) == 0);
  assert(ah);
  
  if(type_has_finalizer((Partial_Reveal_VTable *) decode_vt((VT)ah)))
    return NULL;

#ifdef GC_OBJ_SIZE_STATISTIC
  gc_alloc_statistic_obj_distrubution(size);
#endif
   
  Allocator* allocator = (Allocator*)gc_get_tls();
 
  /* Try to allocate an object from the current Thread Local Block */
  Managed_Object_Handle p_obj;

#if defined(USE_UNIQUE_MARK_SWEEP_GC)

  p_obj = (Managed_Object_Handle)gc_ms_fast_alloc(size, allocator);

#elif defined(USE_UNIQUE_MOVE_COMPACT_GC)

  if ( size > GC_LARGE_OBJ_SIZE_THRESHOLD ) return NULL;
  p_obj = (Managed_Object_Handle)thread_local_alloc(size, allocator);

#else
  /* object shoud be handled specially */
  if ( size > GC_LOS_OBJ_SIZE_THRESHOLD ) return NULL;
  p_obj = (Managed_Object_Handle)thread_local_alloc(size, allocator);

#endif

  if(p_obj == NULL) return NULL;

  assert((((POINTER_SIZE_INT)p_obj) % GC_OBJECT_ALIGNMENT) == 0);
  obj_set_vt((Partial_Reveal_Object*)p_obj, (VT)ah);
  
  return p_obj;
}
